/* FreeRTOS Test Example
*/
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_log.h"


const char *TAG = "FreeRTOS_Test";

static xQueueHandle xqueue_test = NULL;//声明队列句柄

QueueHandle_t QHandle_struct;//声明传输结构体队列
QueueHandle_t QHandle_pointer;//声明传输指针队列

xTaskHandle xTask_One_Handle;
xTaskHandle xTask_Two_Handle;
xTaskHandle xRecvTask;

/* 定义队列传递的结构类型。 */ 
typedef struct A_STRUCT
{
     /* data */
     char id;
     char data;
} xStruct;

#define PIN_INT 0
#define GPIO_INPUT_PIN  (1ULL<<PIN_INT) 
#define ESP_INTR_FLAG_DEFAULT 0

#define SENDER_BIT BIT0

uint8_t pass = 128;
static const char *pcTextForTask2 = "Task 2 is running\t\n";
uint32_t time1=0,time2=0,time3=0,time4=0;

SemaphoreHandle_t xBinarySemaphore;     //声明二值信号量句柄
SemaphoreHandle_t xCountingSemaphore;   //声明计数信号量句柄
SemaphoreHandle_t xMutex;               //声明互斥信号量句柄

EventGroupHandle_t g_event_group;


//GPIO_0中断服务函数
static void IRAM_ATTR gpio_isr_handler(void* arg)
{
    uint32_t gpio_num = (uint32_t) arg;
    //printf("%s\r\n","触发中断");
    time1=esp_timer_get_time(); 
    xSemaphoreGiveFromISR( xBinarySemaphore, NULL);//二值信号量

    xEventGroupSetBits(g_event_group, SENDER_BIT); 
    xQueueSendFromISR(xqueue_test, &gpio_num, NULL); //队列

    xSemaphoreGiveFromISR( xCountingSemaphore,NULL);/*计数信号量*/
    xSemaphoreGiveFromISR( xCountingSemaphore,NULL);
    xSemaphoreGiveFromISR( xCountingSemaphore,NULL); 
}

void gpio_init(){

    gpio_config_t io_conf = {};
          
    //interrupt of rising edge
    io_conf.intr_type = GPIO_INTR_POSEDGE;
    //bit mask of the pins, use GPIO2 here
    io_conf.pin_bit_mask = GPIO_INPUT_PIN;
        //disable pull-down mode
    io_conf.pull_down_en = 0;
        //enable pull-up mode
    io_conf.pull_up_en = 1;
        //set as input mode
    io_conf.mode = GPIO_MODE_INPUT;

    gpio_config(&io_conf);

    //change gpio intrrupt type for one pin
    gpio_set_intr_type(PIN_INT, GPIO_INTR_NEGEDGE);
      
    //install gpio isr service
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    //hook isr handler for specific gpio pin
    gpio_isr_handler_add(PIN_INT, gpio_isr_handler, (void*) PIN_INT);

    ESP_LOGI(TAG, "%s", "GPIO init Over");
}

void vTask_One( void *pvParameters )
{
    uint8_t *rec;
    rec = (uint8_t *)pvParameters;

    ESP_LOGI(TAG, "%s", "任务一运行中...");
    ESP_LOGI(TAG, "传递参数：%d", *rec);  
    
    vTaskDelete(xTask_One_Handle);
}

void vTask_Two( void *pvParameters )
{
    char *pcTaskName;
    pcTaskName = (char *) pvParameters;

    ESP_LOGI(TAG, "%s", "任务二运行中...");
    ESP_LOGI(TAG, "传递参数：%s", pcTaskName); 

    vTaskDelete(xTask_Two_Handle); 

}

/*向队列中发送数据*/
void vQueue_send(void *pvParameters)
{
    uint32_t send_data=24680; 
    vTaskDelay(5000/portTICK_PERIOD_MS);//延时,等待接收任务就绪   
    xQueueSend(xqueue_test,&send_data,(TickType_t)NULL);
   
    vTaskDelete(NULL); 

}

/*接收队列数据*/
void vQueue_receive(void *pvParameters)
{
    uint32_t rec_data;
    while(1)
    {  
        xQueueReceive(xqueue_test,&rec_data, portMAX_DELAY);
        time2=esp_timer_get_time(); 
        ESP_LOGI(TAG, "接收队列消息：%d", time2-time1);   
        vTaskDelay(10/portTICK_PERIOD_MS);//延时,使任务进入阻塞状态,给空闲任务流出时间  
    }
    vTaskDelete(NULL); 

}

void Semaphore_wait(void *pvParameters)
{
    while(1)
    {
        xSemaphoreTake(xBinarySemaphore, portMAX_DELAY);   /*疑问：二值信号创建后会首先触发一次，原因未知*/
        time3=esp_timer_get_time(); 
        ESP_LOGI(TAG, "触发二值信号量:%d",time3-time1); 
        //vTaskDelay(10/portTICK_PERIOD_MS);//延时,使任务进入阻塞状态,给空闲任务流出时间
    }
    vTaskDelete(NULL);
}

void Semaphore_Count(void *pvParameters)//计数信号量任务
{
    while(1)
    {
        xSemaphoreTake(xCountingSemaphore, portMAX_DELAY);   /*疑问：二值信号创建后会首先触发一次，原因未知*/
        time4=esp_timer_get_time(); 
        ESP_LOGI(TAG, "触发计数信号量:%d",time4-time1); 
        //vTaskDelay(10/portTICK_PERIOD_MS);//延时,使任务进入阻塞状态,给空闲任务流出时间
    }
    vTaskDelete(NULL);
}

void Mutex(void *pvParameters)//互斥信号量
{
    xSemaphoreTake( xMutex, portMAX_DELAY );
    {
        char *mutexTask;
        mutexTask = (char *) pvParameters;

        ESP_LOGI(TAG, "传递参数：%s", mutexTask);  
    }
    vTaskDelay(1000/portTICK_PERIOD_MS);//延时,使任务进入阻塞状态,给空闲任务流出时间
    xSemaphoreGive( xMutex );//归还令牌
    vTaskDelete(NULL);
}

void EventGroup(void *pvParameters)//事件组
{
    while(1)
    {    
        EventBits_t bits = xEventGroupWaitBits(g_event_group, SENDER_BIT, pdTRUE, pdFALSE, portMAX_DELAY);
        if (bits & SENDER_BIT) 
        {
            ESP_LOGI(TAG, "触发事件组"); 
        }
    }
    vTaskDelete(NULL);
}


void vSenderTask(void *pvParameters)//发送任务通知
{
    int i;
    int cnt_tx=0;
    int cnt_ok=0;
    int cnt_err=0;
    char c;
    
    while(1)
    {
        if(xRecvTask!=NULL){
            for(i=0;i<3;i++)
            {
                c='a'+cnt_tx;
                cnt_tx++;

                if(xTaskNotify(xRecvTask,(uint32_t)c,eSetValueWithoutOverwrite)== pdPASS)
                    printf("xTaskNotifyGive %d time:OK,val :%c\r\n",cnt_ok++,c);
                else
                    printf("xTaskNotifyGive %d time: ERR,val :%c\r\n",cnt_err++,c);    
            }
        }        
        vTaskDelay(2000);//延时,使任务进入阻塞状态,让接收任务运行
    }    
}

void vReceiverTask(void *pvParameters)//接收任务通知
{
    int cnt_ok=0;
    uint32_t ulValue;
    BaseType_t xResult;
    while(1)
    {
        xResult = xTaskNotifyWait(0,0,&ulValue,portMAX_DELAY);
        if(xResult==pdPASS)
        printf("ulTaskNotifyWait OK: %d, data: %c\r\n",cnt_ok++,(char)ulValue);
        vTaskDelay(100/portTICK_PERIOD_MS);//延时,使任务进入阻塞状态,给空闲任务流出时间
    }

}


/*队列传递结构体发送任务*/
void send_struct_Task(void *pvParam)
{
    QueueHandle_t QHandle;
    QHandle = (QueueHandle_t)pvParam;
    BaseType_t xStatus;
    xStruct xUSB = {1, 55};//结构体声明并赋值
    while (1)
    {
        xStatus = xQueueSend(QHandle, &xUSB, 0);
        if (xStatus != pdPASS)
            printf("send fail\n");
        else
            printf("send done\n");
        xUSB.id++;
        if (xUSB.id == 8)
             xUSB.id = 0;

        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}
/*队列传递结构体接收任务*/
void rec_struct_Task(void *pvParam)
{
    QueueHandle_t QHandle;
    QHandle = (QueueHandle_t)pvParam;
    BaseType_t xStatus;
    xStruct xUSB = {0, 0};
    while (1)
    {
        /* code */
        if (uxQueueMessagesWaiting(QHandle) != 0)
        {
            xStatus = xQueueReceive(QHandle, &xUSB, 0);
            if (xStatus != pdPASS)            
                printf("rec fail\n");
            else
                printf("rec id=%d, data=%d!\n", xUSB.id, xUSB.data);
        }
        else
            printf("no data\n");

        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

/*队列传递指针发送任务*/
void send_pointer_Task(void *pvParam)
{
    QueueHandle_t QHandle;
    QHandle = (QueueHandle_t)pvParam;
    BaseType_t xStatus;
    char *pStrToSend;
    int i = 0;
    while (1)
    {
        pStrToSend = (char *)malloc(50);
        snprintf(pStrToSend, 50, "Today is a good day %d\r\n", i);
        i++;
        xStatus = xQueueSend(QHandle, &pStrToSend, 0);
        if (xStatus != pdPASS)
        {
            printf("send fail\n");
        }
        else
        {
            printf("send done\n");
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

/*队列传递指针接收任务*/
void rec_pointer_Task(void *pvParam)
{
    QueueHandle_t QHandle;
    QHandle = (QueueHandle_t)pvParam;
    BaseType_t xStatus;
    char *pStrToRec;
    while (1)
    {
        /* code */
        if (uxQueueMessagesWaiting(QHandle) != 0)
        {
            xStatus = xQueueReceive(QHandle, &pStrToRec, 0);
            if (xStatus != pdPASS)
            {
                printf("rec fail\n");
            }
            else
            {
                printf("rec %s\n", pStrToRec);
            }
            free(pStrToRec);
        }
        else
        {
            printf("no data\n");
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}


void app_main(void)
{
    gpio_init();

    QHandle_struct= xQueueCreate(5, sizeof(xStruct));//创建传输结构体队列

    QHandle_pointer= xQueueCreate(5, sizeof(char *));//创建传输指针队列
    
    vSemaphoreCreateBinary( xBinarySemaphore );//创建二值信号量

    xCountingSemaphore = xSemaphoreCreateCounting( 10, 0 );//创建计数信号量，最大值为10，初始值为0

    xMutex = xSemaphoreCreateMutex();//创建互斥量
                        
    g_event_group = xEventGroupCreate();// 创建事件组

    //创建队列
    xqueue_test=xQueueCreate(
                            10,             /*队列能够存储的最大单元数目，即队列深度*/
                            sizeof(uint32_t)/*队列中数据单元的长度，以字节为单位*/
                            );

    ESP_LOGI(TAG, "%s", "程序启动...");

    //创建任务一
    xTaskCreate( 
                vTask_One,/*指向任务函数的指针*/
                "Task_1",/* 任务的文本名字，只会在调试中用到 */
                2048,/* 栈深度 */
                (void*)&pass,/* 没有任务参数写NULL */
                5,/* 任务优先级. */
                &xTask_One_Handle/*任务句柄，没有任务句柄时写NULL*/
        );

    //创建任务二
    xTaskCreate( 
                vTask_Two,/*指向任务函数的指针*/
                "Task_2",/* 任务的文本名字，只会在调试中用到 */
                2048,/* 栈深度 */
                (void*)pcTextForTask2,/* 没有任务参数写NULL */
                5,/* 任务优先级. */
                &xTask_Two_Handle/*任务句柄，没有任务句柄时写NULL*/
        );

    /*队列测试任务*/    
    xTaskCreate(vQueue_send,"Queue_send_Task",2048,NULL,6,NULL);//创建任务三，向队列发送数据    
    xTaskCreate(vQueue_receive,"Queue_receive_Task",2048,NULL,6,NULL);//创建任务四，接收队列数据

    /*二值信号量测试任务*/
    if( xBinarySemaphore != NULL ) /* 检查信号量是否成功创建 */
    {        
        xTaskCreate(Semaphore_wait,"Semaphore_wait_Task",2048,NULL,7,NULL);//创建任务五，二值信号量
    }

    /*计数信号量测试任务*/
    if( xCountingSemaphore != NULL ) /* 检查信号量是否成功创建 */
    {        
        xTaskCreate(Semaphore_Count,"Semaphore_Count_Task",2048,NULL,8,NULL);//创建任务六，计数信号量
    }
    
    /*互斥量测试任务*/
    if( xMutex != NULL ) /* 检查信号量是否成功创建 */
    {
        xTaskCreate(Mutex,"Mutex_Task_One",2048,"Mutex_Task_One ****\r\n",8,NULL);//创建任务七，互斥任务一
        xTaskCreate(Mutex,"Mutex_Task_Two",2048,"Mutex_Task_Two ----\r\n",8,NULL);//创建任务八，互斥任务二
    }
    /*事件组测试任务*/
    xTaskCreate(EventGroup,"EventGroup_Task",2048,NULL,5,NULL);//创建任务八，事件组任务

    /*任务通知测试任务-----传输任意值   任务指定运行在内核0*/ 
    xTaskCreatePinnedToCore(vSenderTask,"Sender_Task",2048,NULL,10,NULL,0);//创建任务九，发布任务通知 
    xTaskCreatePinnedToCore(vReceiverTask,"Receiver_Task",2048,NULL,10,&xRecvTask,0);//创建任务十，接收任务通知


    if (QHandle_struct != NULL)//创建传输结构体任务
    {
        printf("create queue successfully\n");
        xTaskCreate(send_struct_Task, "send_struct_Task", 1024 * 2, (void *)QHandle_struct, 6, NULL);
        xTaskCreate(rec_struct_Task, "rec_struct_Task", 1024 * 2, (void *)QHandle_struct, 6, NULL);
    }
    else
    {
        printf("can not create queue\n");
    }

    if (QHandle_pointer != NULL)//创建传输结构体任务
    {
        printf("create queue successfully\n");
        xTaskCreate(send_pointer_Task, "send_pointer_Task", 1024 * 2, (void *)QHandle_pointer, 9, NULL);
        xTaskCreate(rec_pointer_Task, "rec_pointer_Task", 1024 * 2, (void *)QHandle_pointer, 9, NULL);
    }
    else
    {
        printf("can not create queue\n");
    }
}
